package com.atguigu.lease.web.admin.service.impl;

import com.atguigu.lease.common.constant.RedisConstant;
import com.atguigu.lease.common.exception.LeaseException;
import com.atguigu.lease.common.result.ResultCodeEnum;
import com.atguigu.lease.model.entity.*;
import com.atguigu.lease.model.enums.ItemType;
import com.atguigu.lease.web.admin.mapper.ApartmentInfoMapper;
import com.atguigu.lease.web.admin.service.*;
import com.atguigu.lease.web.admin.vo.apartment.ApartmentDetailVo;
import com.atguigu.lease.web.admin.vo.apartment.ApartmentItemVo;
import com.atguigu.lease.web.admin.vo.apartment.ApartmentQueryVo;
import com.atguigu.lease.web.admin.vo.apartment.ApartmentSubmitVo;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.util.Optional;

/**
 * @author xiongkunYang
 * @description 针对表【apartment_info(公寓信息表)】的数据库操作Service实现
 * @createDate 2025-04-19 22:27:34
 */
@Service
@RequiredArgsConstructor
public class ApartmentInfoServiceImpl extends ServiceImpl<ApartmentInfoMapper, ApartmentInfo>
        implements ApartmentInfoService {

    private final ApartmentInfoMapper apartmentInfoMapper;
    private final ProvinceInfoService provinceInfoService;
    private final CityInfoService cityInfoService;
    private final DistrictInfoService districtInfoService;
    private final ApartmentFacilityService apartmentFacilityService;
    private final ApartmentLabelService apartmentLabelService;
    private final ApartmentFeeValueService apartmentFeeValueService;
    private final GraphInfoService graphInfoService;
    private final RoomInfoService roomInfoService;
    private final RedisTemplate<String, Object> redisTemplate;

    /**
     * 保存或更新公寓信息。
     *
     * @param apartmentSubmitVo 包含公寓信息的提交对象，包含以下字段：
     *                          - id: 公寓ID，如果为null表示新增，否则表示更新。
     *                          - facilityInfoIds: 公寓配套设施ID列表。
     *                          - labelIds: 公寓标签ID列表。
     *                          - feeValueIds: 公寓杂项费用值ID列表。
     *                          - graphVoList: 公寓相关图片信息列表。
     * @description 1. 如果是更新操作，先删除与该公寓相关的配套设施、标签、杂项费用值和图片信息。
     * 2. 根据提交的对象重新保存公寓的配套设施、标签、杂项费用值和图片信息。
     */
    @Override
    public Boolean saveOrUpdateApartmentSubmit(ApartmentSubmitVo apartmentSubmitVo) {
        Long apartmentId = apartmentSubmitVo.getId();
        // 前端传过来的数据不包含省市区的名称，需要根据ID查询对应的省市区名称，并填充到对象中
        apartmentSubmitVo.setProvinceName(
                        Optional.ofNullable(provinceInfoService.getById(apartmentSubmitVo.getProvinceId()))
                                .map(ProvinceInfo::getName)
                                .orElse(null))
                .setCityName(
                        Optional.ofNullable(cityInfoService.getById(apartmentSubmitVo.getCityId()))
                                .map(CityInfo::getName)
                                .orElse(null))
                .setDistrictName(
                        Optional.ofNullable(districtInfoService.getById(apartmentSubmitVo.getDistrictId()))
                                .map(DistrictInfo::getName)
                                .orElse(null));
        boolean isSaveOrUpdate = super.saveOrUpdate(apartmentSubmitVo);

        // 如果是更新操作，删除与该公寓相关的旧数据
        if (apartmentId != null) {
            this.removeApartmentRelatedData(apartmentId);
        }

        // 保存公寓配套设施信息
        Optional.ofNullable(apartmentSubmitVo.getFacilityInfoIds())
                .filter(ids -> !ids.isEmpty())
                .map(ids -> ids.stream()
                        .map(facilityId -> ApartmentFacility.builder()
                                .apartmentId(apartmentSubmitVo.getId()).facilityId(facilityId)
                                .build())
                        .toList())
                .ifPresent(this.apartmentFacilityService::saveBatch);

        // 保存公寓标签信息
        Optional.ofNullable(apartmentSubmitVo.getLabelIds())
                .filter(ids -> !ids.isEmpty())
                .map(ids -> ids.stream()
                        .map(labelId -> ApartmentLabel.builder()
                                .apartmentId(apartmentSubmitVo.getId()).labelId(labelId)
                                .build())
                        .toList())
                .ifPresent(this.apartmentLabelService::saveBatch);

        // 保存公寓杂项费用值信息
        Optional.ofNullable(apartmentSubmitVo.getFeeValueIds())
                .filter(ids -> !ids.isEmpty())
                .map(ids -> ids.stream()
                        .map(feeValueId -> ApartmentFeeValue.builder()
                                .apartmentId(apartmentSubmitVo.getId()).feeValueId(feeValueId)
                                .build())
                        .toList())
                .ifPresent(this.apartmentFeeValueService::saveBatch);

        // 保存公寓图片信息
        Optional.ofNullable(apartmentSubmitVo.getGraphVoList())
                .filter(graphVos -> !graphVos.isEmpty())
                .map(graphVos -> graphVos.stream()
                        .map(graphVo -> GraphInfo.builder()
                                .name(graphVo.getName()).url(graphVo.getUrl()).itemType(ItemType.APARTMENT).itemId(apartmentSubmitVo.getId())
                                .build())
                        .toList())
                .ifPresent(this.graphInfoService::saveBatch);

        return isSaveOrUpdate;
    }

    @Override
    public IPage<ApartmentItemVo> apartmentItemPageByQuery(long current, long size, ApartmentQueryVo queryVo) {
        Page<ApartmentItemVo> page = new Page<>(current, size);
        return this.apartmentInfoMapper.selectApartmentItemPageByQuery(page, queryVo);
    }

    @Override
    public ApartmentDetailVo getApartmentDetailById(Long id) {
        return this.apartmentInfoMapper.selectApartmentDetailById(id);
    }

    /**
     * 根据id删除公寓信息
     * 首先判断该公寓下是否存在房间信息，如果存在，则抛出删除错误异常
     * 否则，将从图片信息、公寓标签、公寓设施和公寓费用值中删除所有与该公寓ID相关的信息
     * 最后，按ID删除公寓信息
     *
     * @param id 公寓的唯一标识符
     * @return 如果删除成功，则返回true；否则返回false
     * @throws LeaseException 如果公寓在房间信息中存在时尝试删除，则抛出此异常
     */
    @Override
    public Boolean removeApartmentInfoById(Long id) {
        // 判断该公寓下是否存在房间信息
        boolean existed = roomInfoService.exists(
                new LambdaQueryWrapper<RoomInfo>()
                        .eq(RoomInfo::getApartmentId, id));
        // 如果存在，则抛出删除错误异常
        if (existed) {
            throw new LeaseException(ResultCodeEnum.DELETE_ERROR);
        }

        this.removeApartmentRelatedData(id);

        // 按ID删除公寓信息
        return super.removeById(id);
    }

    /**
     * 删除与公寓相关的所有关联数据。
     *
     * @param apartmentId 公寓ID
     */
    private void removeApartmentRelatedData(Long apartmentId) {
        this.apartmentFacilityService.remove(
                new LambdaQueryWrapper<ApartmentFacility>()
                        .eq(ApartmentFacility::getApartmentId, apartmentId));

        this.apartmentLabelService.remove(
                new LambdaQueryWrapper<ApartmentLabel>()
                        .eq(ApartmentLabel::getApartmentId, apartmentId));

        this.apartmentFeeValueService.remove(
                new LambdaQueryWrapper<ApartmentFeeValue>()
                        .eq(ApartmentFeeValue::getApartmentId, apartmentId));

        this.graphInfoService.remove(
                new LambdaQueryWrapper<GraphInfo>()
                        .eq(GraphInfo::getItemType, ItemType.APARTMENT)
                        .eq(GraphInfo::getItemId, apartmentId));
        // 删除Redis缓存
        this.redisTemplate.delete(RedisConstant.APP_APARTMENT_PREFIX + apartmentId);
    }
}




